﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/10 12:06:28
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ImageK.Measure;
using ImageK.Plugin;
using ImageK.Plugin.Filter;
using ImageK.Process;

namespace ImageK.Gui
{
    /// <summary>
    /// An Overlay is a list of ROIs that can be drawn non-destructively on an Image.
    /// </summary>
    public class Overlay : List<Roi>
    {
        private bool _label;

        private bool _drawNames;
        private bool _drawBackgrounds;
        private Color labelColor;
        private Font labelFont;
        private bool _scalableLabels;
        private bool _isCalibrationBar;

        private bool _selectable = true;
        private bool _draggable = true;

        /** Constructs an empty Overlay. */
        public Overlay()
        {
            
        }

        /** Constructs an Overlay and adds the specified ROI. */
        public Overlay(Roi roi)
        {
            if (roi!=null)
                this.Add(roi);
        }

        /** Adds an ROI to this Overlay. */
        public void add(Roi roi)
        {
            if (roi!=null)
                this.Add(roi);
        }

        /** Adds an ROI to this Overlay using the specified name. */
        public void add(Roi roi, String name)
        {
            roi.setName(name);
            add(roi);
        }

        /** Adds an ROI to this Overlay. */
        public void addElement(Roi roi)
        {
            if (roi!=null)
                this.Add(roi);
        }

        /** Replaces the ROI at the specified index. */
        public void set(Roi roi, int index)
        {
            if (index<0 || index>=this.Count)
                throw new ArgumentException("set: index out of range");
            if (roi!=null)
                this[index] = roi;
        }

        /** Removes the ROI with the specified index from this Overlay. */
        public void remove(int index)
        {
            if (index>=0)
                this.RemoveAt(index);
        }

        /** Removes the specified ROI from this Overlay. */
        public void remove(Roi roi)
        {
            this.Remove(roi);
        }

        /** Removes all ROIs that have the specified name. */
        public void remove(String name)
        {
            if (name==null) return;
            for (int i = size()-1; i>=0; i--)
            {
                if (name.Equals(get(i).getName()))
                    remove(i);
            }
        }

        /** Removes all the ROIs in this Overlay. */
        public void clear()
        {
            this.Clear();
        }

        /** Returns the ROI with the specified index or null if the index is invalid. */
        public Roi get(int index)
        {
            try
            {
                return (Roi)this[index];
            }
            catch (Exception e)
            {
                return null;
            }
        }

        /** Returns the ROI with the specified name or null if not found. */
        public Roi get(String name)
        {
            int index = getIndex(name);
            if (index==-1)
                return null;
            else
                return get(index);
        }

        /** Returns the index of the ROI with the specified name, or -1 if not found. */
        public int getIndex(String name)
        {
            if (name==null) return -1;
            Roi[] rois = toArray();
            for (int i = rois.Length-1; i>=0; i--)
            {
                if (name.Equals(rois[i].getName()))
                    return i;
            }
            return -1;
        }

        /** Returns the index of the last ROI that contains the point (x,y)
            or null if no ROI contains the point. */
        public int indexAt(int x, int y)
        {
            Roi[] rois = toArray();
            for (int i = rois.Length-1; i>=0; i--)
            {
                if (contains(rois[i], x, y))
                    return i;
            }
            return -1;
        }

        private bool contains(Roi roi, int x, int y)
        {
            if (roi==null) return false;
            if (roi is Line)
			    return (((Line)roi).getFloatPolygon(10)).contains(x, y);
            else
                return roi.contains(x, y);
        }

        /** Returns 'true' if this Overlay contains the specified ROI. */
        public bool contains(Roi roi)
        {
            return this.Contains(roi);
        }

        /** Returns the number of ROIs in this Overlay. */
        public int size()
        {
            return this.Count;
        }

        /** Returns on array containing the ROIs in this Overlay. */
        public Roi[] toArray()
        {
            return (Roi[])this.ToArray();
        }

        /** Returns on array containing the ROIs with the specified indexes. */
        public Roi[] toArray(int[] indexes)
        {
            ArrayList rois = new ArrayList();
            for (int i = 0; i<size(); i++)
            {
                if (indexes[i]>=0 && indexes[i]<size())
                    rois.Add(get(indexes[i]));
            }
            return (Roi[])rois.ToArray();
        }

        /** Sets the stroke color of all the ROIs in this overlay. */
        public void setStrokeColor(Color color)
        {
            for (int i = 0; i<size(); i++)
                get(i).setStrokeColor(color);
        }

        /** Sets the stroke width of all the ROIs in this overlay. */
        public void setStrokeWidth(Double width)
        {
            for (int i = 0; i<size(); i++)
                get(i).setStrokeWidth(width);
        }

        /** Sets the fill color of all the ROIs in this overlay. */
        public void setFillColor(Color color)
        {
            for (int i = 0; i<size(); i++)
                get(i).setFillColor(color);
        }

        /** Moves all the ROIs in this overlay. */
        public void translate(int dx, int dy)
        {
            for (int i = 0; i<size(); i++)
                get(i).translate(dx, dy);
        }

        /** Moves all the ROIs in this overlay.
        * Marcel Boeglin, October 2013
        */
        public void translate(double dx, double dy)
        {
            for (int i = 0; i<size(); i++)
                get(i).translate(dx, dy);
        }

        /** Measures the ROIs in this overlay on the specified image
        * and returns the results as a ResultsTable.
        */
        public ResultsTable measure(ImagePlus imp)
        {
            ResultsTable rt = new ResultsTable();
            rt.showRowNumbers(true);
            Analyzer analyzer = new Analyzer(imp, rt);
            for (int i = 0; i<size(); i++)
            {
                Roi roi = get(i);
                imp.setRoi(roi);
                analyzer.measure();
            }
            imp.deleteRoi();
            return rt;
        }

        /*
        * Duplicate the elements of this overlay which  
        * intersect with the rectangle 'bounds'.
        * Author: Wilhelm Burger
        * Author: Marcel Boeglin
        */
        public Overlay crop(Rectangle bounds)
        {
            if (bounds==Rectangle.Empty)
                return duplicate();
            Overlay overlay2 = create();
            Roi[] allRois = toArray();
            foreach (Roi roi in allRois)
            {
                Rectangle roiBounds = roi.getBounds();
                if (roiBounds.Width==0) roiBounds.Width=1;
                if (roiBounds.Height==0) roiBounds.Height=1;
                if (bounds.IntersectsWith(roiBounds))
                    overlay2.add((Roi)roi.clone());
            }
            int dx = bounds.X>0 ? bounds.X : 0;
            int dy = bounds.Y>0 ? bounds.Y : 0;
            if (dx>0 || dy>0)
                overlay2.translate(-dx, -dy);
            return overlay2;
        }

        /** Removes ROIs having positions outside of the  
        * interval defined by firstSlice and lastSlice.
        * Marcel Boeglin, September 2013
        */
        public void crop(int firstSlice, int lastSlice)
        {
            for (int i = size()-1; i>=0; i--)
            {
                Roi roi = get(i);
                int position = roi.getPosition();
                if (position>0)
                {
                    if (position<firstSlice || position>lastSlice)
                        remove(i);
                    else
                        roi.setPosition(position-firstSlice+1);
                }
            }
        }

        /** Removes ROIs having a C, Z or T coordinate outside the volume
        * defined by firstC, lastC, firstZ, lastZ, firstT and lastT.
        * Marcel Boeglin, September 2013
        */
        public void crop(int firstC, int lastC, int firstZ, int lastZ, int firstT, int lastT)
        {
            int nc = lastC-firstC+1, nz = lastZ-firstZ+1, nt = lastT-firstT+1;
            bool toCStack = nz==1 && nt==1;
            bool toZStack = nt==1 && nc==1;
            bool toTStack = nc==1 && nz==1;
            Roi roi;
            int c, z, t, c2, z2, t2;
            for (int i = size()-1; i>=0; i--)
            {
                roi = get(i);
                c = roi.getCPosition();
                z = roi.getZPosition();
                t = roi.getTPosition();
                c2 = c-firstC+1;
                z2 = z-firstZ+1;
                t2 = t-firstT+1;
                if (toCStack)
                    roi.setPosition(c2);
                else if (toZStack)
                    roi.setPosition(z2);
                else if (toTStack)
                    roi.setPosition(t2);
                else
                    roi.setPosition(c2, z2, t2);
                if ((c2<1||c2>nc) && c>0 || (z2<1||z2>nz) && z>0 || (t2<1||t2>nt) && t>0)
                    remove(i);
            }
        }

        /** Returns the bounds of this overlay. */
        /*
        public Rectangle getBounds() {
            if (size()==0)
                return new Rectangle(0,0,0,0);
            int xmin = Integer.MAX_VALUE;
            int xmax = -Integer.MAX_VALUE;
            int ymin = Integer.MAX_VALUE;
            int ymax = -Integer.MAX_VALUE;
            Roi[] rois = toArray();
            for (int i=0; i<rois.length; i++) {
                Rectangle r = rois[i].getBounds();
                if (r.x<xmin) xmin = r.x;
                if (r.y<ymin) ymin = r.y;
                if (r.x+r.width>xmax) xmax = r.x+r.width;
                if (r.y+r.height>ymax) ymax = r.y+r.height;
            }
            return new Rectangle(xmin, ymin, xmax-xmin, ymax-ymin);
        }
        */

        /* Returns the Roi that results from XORing all the ROIs
         * in this overlay that have an index in the array ‘indexes’.
        */
        public Roi xor(int[] indexes)
        {
            return Roi.xor(toArray(indexes));
        }

        /** Returns a new Overlay that has the same properties as this one. */
        public Overlay create()
        {
            Overlay overlay2 = new Overlay();
            overlay2.drawLabels(_label);
            overlay2.drawNames(_drawNames);
            overlay2.drawBackgrounds(_drawBackgrounds);
            overlay2.setLabelColor(labelColor);
            overlay2.setLabelFont(labelFont, _scalableLabels);
            overlay2.setIsCalibrationBar(_isCalibrationBar);
            overlay2.selectable(_selectable);
            overlay2.setDraggable(_draggable);
            return overlay2;
        }

        /** Returns a clone of this Overlay. */
        public Overlay duplicate()
        {
            throw new NotImplementedException();
            // Roi[] rois = toArray();
            // Overlay overlay2 = create();
            // for (int i = 0; i < rois.Length; i++)
            //     overlay2.add((Roi)rois[i].clone());
            // return overlay2;
        }

        /** Returns a scaled version of this Overlay. */
        public Overlay scale(double xscale, double yscale)
        {
            Overlay overlay2 = create();
            for (int i = 0; i<size(); i++)
            {
                Roi roi = get(i);
                int position = roi.getPosition();
                roi = RoiScaler.scale(roi, xscale, yscale, false);
                roi.setPosition(position);
                overlay2.add(roi);
            }
            return overlay2;
        }

        /** Returns a rotated version of this Overlay. */
        public Overlay rotate(double angle, double xcenter, double ycenter)
        {
            //IJ.log("rotate: "+angle+" "+xcenter+" "+ycenter);
            throw new NotImplementedException();
            // Overlay overlay2 = create();
            // for (int i = 0; i<size(); i++)
            // {
            //     Roi roi = get(i);
            //     int position = roi.getPosition();
            //     if (!Rotator.GRID.equals(roi.getName()))
            //         roi = RoiRotator.rotate(roi, angle, xcenter, ycenter);
            //     roi.setPosition(position);
            //     overlay2.add(roi);
            // }
            // return overlay2;
        }

        public void drawLabels(bool b)
        {
            _label = b;
        }

        public bool getDrawLabels()
        {
            return _label;
        }

        public void drawNames(bool b)
        {
            _drawNames = b;
            Roi[] rois = toArray();
            for (int i = 0; i<rois.Length; i++)
                rois[i].setIgnoreClipRect(_drawNames);
        }

        public bool getDrawNames()
        {
            return _drawNames;
        }

        public void drawBackgrounds(bool b)
        {
            _drawBackgrounds = b;
        }

        public bool getDrawBackgrounds()
        {
            return _drawBackgrounds;
        }

        public void setLabelColor(Color c)
        {
            labelColor = c;
        }

        public Color getLabelColor()
        {
            return labelColor;
        }

        public void setLabelFont(Font font)
        {
            setLabelFont(font, false);
        }

        public void setLabelFont(Font font, bool scalable)
        {
            labelFont = font;
            _scalableLabels = scalable;
        }

        /** Set the label font size with options. The options string can contain
         * 'scale' (enlarge labels when image zoomed), 'bold'
         * (display bold labels) or 'background' (display labels
         * with contrasting background.
        */
        public void setLabelFontSize(int size, String options)
        {
            var style = FontStyle.Regular;
            if (options!=null)
            {
                _scalableLabels = options.Contains("scale");
                if (options.Contains("bold"))
                    style = FontStyle.Bold;
                _drawBackgrounds = options.Contains("back");
            }
            labelFont = new Font("SansSerif", size, style);
            drawLabels(true);
        }

        public Font getLabelFont()
        {
            return labelFont;
        }

        public void setIsCalibrationBar(bool b)
        {
            this._isCalibrationBar = b;
        }

        public bool isCalibrationBar()
        {
            return _isCalibrationBar;
        }

        /** Fills all the ROIs in this overlay with 'foreground' after clearing the
            the image to 'background' if it is not null. */
        public void fill(ImagePlus imp, Color foreground, Color background)
        {
            ImageProcessor ip = imp.getProcessor();
            if (background!=null)
            {
                ip.resetRoi();
                ip.setColor(background);
                ip.fillRect(0, 0, ip.getWidth(), ip.getHeight());
            }
            if (foreground!=null)
            {
                ip.setColor(foreground);
                for (int i = 0; i<size(); i++)
                    ip.fill(get(i));
                ip.resetRoi();
            }
            imp.updateAndDraw();
        }

        void setVector(List<Roi> v)
        {
            this.clear(); 
            this.AddRange(v);
        }

        List<Roi> getVector() { return this; }

        /** Set 'false' to prevent ROIs in this overlay from being activated 
        by clicking on their labels or by a long clicking. */
        public void selectable(bool selectable)
        {
            this._selectable = selectable;
        }

        /** Returns 'true' if ROIs in this overlay can be activated
            by clicking on their labels or by a long press. */
        public bool isSelectable()
        {
            return _selectable;
        }

        /** Set 'false' to prevent ROIs in this overlay from being dragged by their labels. */
        public void setDraggable(bool draggable)
        {
            this._draggable = draggable;
        }

        /** Returns 'true' if ROIs in this overlay can be dragged by their labels. */
        public bool isDraggable()
        {
            return _draggable;
        }

        public bool scalableLabels()
        {
            return _scalableLabels;
        }




    }
}
